package org.jtb.quickr;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Logger;
import org.jdom.Element;

/**
 * Photo response.
 *
 * @see org.jtb.quickr.PhotosService#getInfo(String)
 * @see org.jtb.quickr.PhotosService#getInfo(String,String)
 */
public class PhotoResponse extends FlickrResponse {
    /**
     * Photo owner response.
     */
    public static class Owner extends FlickrResponse {
        private String userId;
        private String userName;
        private String realName;
        private String location;
        
        Owner(Element e) throws QuickrException {
            super(e);
        }
        
        protected void populate(Element e) {
            Element ownerElement = e.getChild("owner");
            
            this.userId = ownerElement.getAttributeValue("nsid");
            this.userName = ownerElement.getAttributeValue("username");
            this.realName = ownerElement.getAttributeValue("realname");
            this.location = ownerElement.getAttributeValue("location");
        }
        
        
        public String getUserId() {
            return userId;
        }
        
        public String getUserName() {
            return userName;
        }
        
        public String getRealName() {
            return realName;
        }
        
        public String getLocation() {
            return location;
        }
    }
    
    /**
     * Photo visibility response.
     */
    public static class Visibility extends FlickrResponse {
        private boolean pub;
        private boolean friend;
        private boolean family;
        
        Visibility(Element e) throws QuickrException {
            super(e);
        }
        
        protected void populate(Element e) {
            Element ve = e.getChild("visibility");
            
            this.pub = getAttrBoolean(ve, "ispublic");
            this.friend = getAttrBoolean(ve, "isfriend");
            this.family = getAttrBoolean(ve, "isfamily");
        }
        
        public boolean isPub() {
            return pub;
        }
        
        public boolean isFriend() {
            return friend;
        }
        
        public boolean isFamily() {
            return family;
        }
    }
    
    /**
     * Photo date response.
     */
    public static class Dates extends FlickrResponse {
        private Date posted;
        private String taken;
        private int takenGranularity;
        private Date lastUpdate;
        
        Dates(Element e) throws QuickrException {
            super(e);
        }
        
        protected void populate(Element e) {
            Element oe = e.getChild("dates");
            
            this.posted = getAttrDate(oe, "posted");
            this.taken = oe.getAttributeValue("taken");
            this.takenGranularity = getAttrInt(oe, "takengranularity");
            this.lastUpdate = getAttrDate(oe, "lastupdate");
        }
        
        public Date getPosted() {
            return posted;
        }
        
        public String getTaken() {
            return taken;
        }
        
        public int getTakenGranularity() {
            return takenGranularity;
        }
        
        public Date getLastUpdate() {
            return lastUpdate;
        }
    }
    
    /**
     * Photo visibility response.
     */
    public static class Permissions extends FlickrResponse {
        private int comment = 0;
        private int addMeta = 0;
        
        Permissions(Element e) throws QuickrException {
            super(e);
        }
        
        protected void populate(Element e) {
            Element pe = e.getChild("permissions");
            if (pe == null) {
                return;
            }
            
            this.comment = getAttrInt(pe, "permcomment");
            this.addMeta = getAttrInt(pe, "permaddmeta");
        }
        
        public int getComment() {
            return comment;
        }
        
        public int getAddMeta() {
            return addMeta;
        }
    }
    
    /**
     * Photo editability response.
     */
    public static class Editability extends FlickrResponse {
        private boolean canComment;
        private boolean canAddMeta;
        
        Editability(Element e) throws QuickrException {
            super(e);
        }
        
        protected void populate(Element e) {
            Element ee = e.getChild("editability");
            
            this.canComment = getAttrBoolean(ee, "cancomment");
            this.canAddMeta = getAttrBoolean(ee, "canaddmeta");
        }
        
        public boolean isCanComment() {
            return canComment;
        }
        
        public boolean isCanAddMeta() {
            return canAddMeta;
        }
    }
    
    /**
     * Photo notes response.
     */
    public static class Notes extends FlickrResponse {
        /**
         * Photo notes note response.
         */
        public static class Note extends FlickrResponse {
            private String id;
            private String author;
            private String authorName;
            private int x;
            private int y;
            private int w;
            private int h;
            private String note;
            
            Note(Element e) throws QuickrException {
                super(e);
            }
            
            protected void populate(Element e) throws QuickrException {
                this.id = e.getAttributeValue("id");
                this.author = e.getAttributeValue("author");
                this.authorName = e.getAttributeValue("authorname");
                this.x = getAttrInt(e, "x");
                this.y = getAttrInt(e, "y");
                this.w = getAttrInt(e, "w");
                this.h = getAttrInt(e, "h");
                
                this.note = getBodyString(e);
            }
            
            public String getId() {
                return id;
            }
            
            public String getAuthor() {
                return author;
            }
            
            public String getAuthorName() {
                return authorName;
            }
            
            public int getX() {
                return x;
            }
            
            public int getY() {
                return y;
            }
            
            public int getW() {
                return w;
            }
            
            public int getH() {
                return h;
            }
            
            public String getNote() {
                return note;
            }
        }
        
        private List<Note> notes;
        
        Notes(Element e) throws QuickrException {
            super(e);
        }
        
        protected void populate(Element e) throws QuickrException {
            Element ne = e.getChild("notes");
            
            notes = new ArrayList<Note>();
            List noteElements = ne.getChildren("note");
            for (Iterator i = noteElements.iterator(); i.hasNext(); ) {
                Element noteElement = (Element)i.next();
                Note n = new Note(noteElement);
                getNotes().add(n);
            }
        }
        
        public List<Note> getNotes() {
            return notes;
        }
    }
    
    /**
     * Photo tag response.
     */
    public static class Tags extends FlickrResponse {
        /**
         * Photo tags tag response.
         */
        public static class Tag extends FlickrResponse {
            private String id;
            private String author;
            private String raw;
            private String tag;
            
            Tag(Element e) throws QuickrException {
                super(e);
            }
            
            protected void populate(Element e) {
                this.id = e.getAttributeValue("id");
                this.author = e.getAttributeValue("author");
                this.raw = e.getAttributeValue("raw");
                
                this.tag = getBodyString(e);
            }

            public String getId() {
                return id;
            }

            public String getAuthor() {
                return author;
            }

            public String getRaw() {
                return raw;
            }

            public String getTag() {
                return tag;
            }
        }
        
        private List<Tag> tags;
        
        Tags(Element e) throws QuickrException {
            super(e);
        }
        
        protected void populate(Element e) throws QuickrException {
            Element te = e.getChild("tags");
            
            tags = new ArrayList<Tag>();
            List tagElements = te.getChildren("tag");
            for (Iterator i = tagElements.iterator(); i.hasNext(); ) {
                Element tagElement = (Element)i.next();
                Tag t = new Tag(tagElement);
                tags.add(t);
            }
        }
        
        public List<Tag> getTags() {
            return tags;
        }
    }
    
    /**
     * Photo URLs response.
     */
    public static class Urls extends FlickrResponse {
        /**
         * Photo URLs URL response.
         */
        public static class Url extends FlickrResponse {
            private String type;
            private URL url;
            
            Url(Element e) throws QuickrException {
                super(e);
            }
            
            protected void populate(Element e) throws QuickrException {
                this.type = e.getAttributeValue("type");
                try {
                    this.url = new URL(getBodyString(e));
                } catch (MalformedURLException mfue) {
                    throw new QuickrException("could not construct URL: " + getBodyString(e));
                }
            }

            public String getType() {
                return type;
            }

            public URL getUrl() {
                return url;
            }
        }
        
        private List<Url> urls;
        
        Urls(Element e) throws QuickrException {
            super(e);
        }
        
        protected void populate(Element e) throws QuickrException {
            Element ue = e.getChild("urls");
            
            urls = new ArrayList<Url>();
            List urlElements = ue.getChildren("url");
            for (Iterator i = urlElements.iterator(); i.hasNext(); ) {
                Element urlElement = (Element)i.next();
                Url u = new Url(urlElement);
                urls.add(u);
            }
        }
        
        public List<Url> getUrls() {
            return urls;
        }
    }
    
    private static Logger logger = Logger.getLogger(PhotoResponse.class.getName());
    
    private String photoId;
    private String photoSecret;
    private String server;
    private boolean favorite;
    private String license;
    private int rotation;
    private String originalFormat;
    private Owner owner;
    private String title;
    private String description;    
    private Visibility visibility;
    private Dates dates;
    private Permissions permissions;
    private Editability editability;
    private int comments;
    private Notes notes;
    private Tags tags;
    private Urls urls;
    
    private String staticUrl;
    
    PhotoResponse(InputStream is, String staticUrl) throws QuickrException {
        this.staticUrl = staticUrl;
        init(is);
    }
    
    protected void populate(Element e) throws QuickrException {
        Element photoElement = e.getChild("photo");
        
        this.photoId = photoElement.getAttributeValue("id");
        this.photoSecret = photoElement.getAttributeValue("secret");
        this.server = photoElement.getAttributeValue("server");
        this.favorite = getAttrBoolean(photoElement, "favorite");
        this.license = photoElement.getAttributeValue("license");
        this.rotation = getAttrInt(photoElement, "rotation");
        this.originalFormat = photoElement.getAttributeValue("originalformat");
        
        this.title = getBodyString(photoElement, "title");
        this.description = getBodyString(photoElement, "description");
        this.comments = getBodyInt(photoElement, "comments");
        
        this.owner = new Owner(photoElement);
        this.visibility = new Visibility(photoElement);
        this.dates = new Dates(photoElement);
        this.permissions = new Permissions(photoElement);
        this.editability = new Editability(photoElement);
        this.notes = new Notes(photoElement);
        this.tags = new Tags(photoElement);
        this.urls = new Urls(photoElement);
        
    }
    
    /**
     * Get the photo ID.
     * @return The photo ID.
     */
    public String getPhotoId() {
        return photoId;
    }
    
    /**
     * Get the photo secret.
     * @return The photo secret.
     */
    public String getPhotoSecret() {
        return photoSecret;
    }
    
    /**
     * The the photo server.
     * @return The photo server.
     */
    public String getServer() {
        return server;
    }
    
    /**
     * Is the photo a favorite?
     * @return If the photo is a favorite.
     */
    public boolean isFavorite() {
        return favorite;
    }
    
    /**
     * Get the photo license.
     * @return The photo license.
     */
    public String getLicense() {
        return license;
    }
    
    /**
     * Get photo rotation.
     * @return The photo rotation.
     */
    public int getRotation() {
        return rotation;
    }
    
    /**
     * Get the photo original format.
     * @return The original format.
     */
    public String getOriginalFormat() {
        return originalFormat;
    }
    
    /**
     * Get the photo owner.
     * @return The photo owner.
     */
    public Owner getOwner() {
        return owner;
    }
    
    /**
     * Get the photo title.
     * @return The photo title.
     */
    public String getTitle() {
        return title;
    }    
    
    /**
     * Get the photo description.
     * @return The photo description.
     */
    public String getDescription() {
        return description;
    }
    
    /**
     * Get the photo visibility.
     * @return The photo visibility.
     */
    public Visibility getVisibility() {
        return visibility;
    }
    
    /**
     * Get the photo dates.
     * @return The photo dates.
     */
    public Dates getDates() {
        return dates;
    }
    
    /**
     * Get photo permissions.
     * @return The permissions.
     */
    public Permissions getPermissions() {
        return permissions;
    }
    
    /**
     * Get photo editability.
     * @return The photo editability.
     */
    public Editability getEditability() {
        return editability;
    }
    
    /**
     * Get photo comments.
     * @return The comments.
     */
    public int getComments() {
        return comments;
    }
    
    /**
     * Get photo notes.
     * @return The notes.
     */
    public Notes getNotes() {
        return notes;
    }
    
    /**
     * Get photo tags.
     * @return The tags.
     */
    public Tags getTags() {
        return tags;
    }
    
    /**
     * Get photo URLs.
     * @return The URLs.
     */
    public Urls getUrls() {
        return urls;
    }
        
    /**
     * Get scaled photo URL.
     * @param size Photo size.
     * @throws QuickrException If a problem occurs.
     * @return The photo URL.
     */
    public URL getUrl(PhotoSize size) throws QuickrException {
        PhotoUrl purl = new PhotoUrl(staticUrl, getServer(), getPhotoId(), getPhotoSecret(), size);
        return purl.getUrl();
    }
    
    /**
     * Get thumbnail photo URL.
     * This is equivalend to <code>getUrl(PhotoSize.THUMBNAIL)</code>
     * @throws QuickrException If a problem occurs.
     * @return The photo URL.
     * @see #getUrl(PhotoSize)
     */
    public URL getThumbnailUrl() throws QuickrException {
        return getUrl(PhotoSize.THUMBNAIL);
    }
    
    /**
     * Get small square photo URL.
     * This is equivalend to <code>getUrl(PhotoSize.SMALL_SQUARE)</code>
     * @throws QuickrException If a problem occurs.
     * @return The photo URL.
     * @see #getUrl(PhotoSize)
     */
    public URL getSmallSquareUrl() throws QuickrException {
        return getUrl(PhotoSize.SMALL_SQUARE);
    }
    
    /**
     * Get small photo URL.
     * This is equivalend to <code>getUrl(PhotoSize.SMALL)</code>
     * @throws QuickrException If a problem occurs.
     * @return The photo URL.
     * @see #getUrl(PhotoSize)
     */
    public URL getSmallUrl() throws QuickrException {
        return getUrl(PhotoSize.SMALL);
    }
    
    /**
     * Get medium photo URL.
     * This is equivalend to <code>getUrl(PhotoSize.MEDIUM)</code>
     * @throws QuickrException If a problem occurs.
     * @return The photo URL.
     * @see #getUrl(PhotoSize)
     */
    public URL getMediumUrl() throws QuickrException {
        return getUrl(PhotoSize.MEDIUM);
    }
    
    /**
     * Get large photo URL.
     * This is equivalend to <code>getUrl(PhotoSize.LARGE)</code>
     * @throws QuickrException If a problem occurs.
     * @return The photo URL.
     * @see #getUrl(PhotoSize)
     */
    public URL getLargeUrl() throws QuickrException {
        return getUrl(PhotoSize.LARGE);
    }
    
    /**
     * Get original photo URL.
     * @throws QuickrException If a problem occurs.
     * @return The photo URL.
     */
    public URL getOriginalUrl() throws QuickrException {
        PhotoUrl purl = new PhotoUrl(staticUrl, getServer(), getPhotoId(), getPhotoSecret(), getOriginalFormat());
        return purl.getUrl();
    }
}
